package stackandqueue.stack.application.arithmetic;

import stackandqueue.stack.sequentialstack.SqStack;

import java.util.Scanner;

/**
 * 栈的应用-四则运算表达式求值
 */
public class ArithUtils {

    /**
     * 进行后缀表达式运算
     *
     * @param arguments
     * @return
     */
    public double suffixCount(String[] arguments) {
        Double result;
        SqStack<Double> sqStack = new SqStack<>();
        for (int i = 0; i < arguments.length; i++) {
            if (!isOperator(arguments[i])) {
                sqStack.push(Double.valueOf(arguments[i]));
            } else {
                result = countWithOperator(sqStack.pop(), sqStack.pop(), arguments[i]);
                sqStack.push(result);
            }
        }
        return sqStack.pop();
    }

    /**
     * 中缀表达式计算
     *
     * @param infixExpr
     * @return
     */
    public Double infixCount(String infixExpr) {
        String[] suffixExpr = infix2suffix(infixExpr);
        return suffixCount(suffixExpr);
    }

    /**
     * 判断是否是运算符号
     *
     * @param argument
     * @return
     */
    private boolean isOperator(String argument) {
        return "+".equals(argument) || "-".equals(argument)
                || "*".equals(argument) || "/".equals(argument)
                || "(".equals(argument) || ")".equals(argument);
    }

    /**
     * 根据符符号进行计算
     *
     * @param num1     运算时的后一个数
     * @param num2     运算时的前一个数
     * @param operator 运算符号
     * @return
     */
    private Double countWithOperator(Double num1, Double num2, String operator) {
        Double countResult;
        switch (operator) {
            case "+":
                countResult = num2 + num1;
                break;
            case "-":
                countResult = num2 - num1;
                break;
            case "*":
                countResult = num2 * num1;
                break;
            case "/":
                countResult = num2 / num1;
                break;
            default:
                countResult = null;
                break;
        }
        return countResult;
    }

    /**
     * 中缀表达式转后缀表达式
     *
     * @param infixExpr 中缀表达式
     * @return 返回后缀表达式的数组
     */
    public String[] infix2suffix(String infixExpr) {
        String[] result = new String[1024];
        SqStack<String> oprStack = new SqStack<>();
        int i = 0;
        String[] infixArrayExpr = string2ArrayExpr(infixExpr); // 获取中缀表达式数组
        for (String s : infixArrayExpr) {
            if (!isOperator(s)) { // 如果是数字，直接输出
                result[i++] = s;
            } else {
                if ("(".equals(s) || compareOperator(s, oprStack.getTop()) > 0) { // 如果是"("或者优先级高于栈顶符号，则直接压入栈中
                    oprStack.push(s);
                } else {
                    if (")".equals(s)) { // 如果是")"符号要入栈，则将栈顶符号依次输出，直到匹配到"("
                        while (!("(".equals(oprStack.getTop()))) {
                            result[i++] = oprStack.pop();
                        }
                        oprStack.pop(); // 将匹配到的"("输出
                    } else { // 如果是运算符，且优先级低于或等于栈顶符号，则输出栈顶符号，直到栈顶符号为"("或者优先级低于要入栈元素
                        while (!oprStack.stackEmpty()
                                && !("(".equals(oprStack.getTop())) && compareOperator(oprStack.getTop(), s) >= 0) {
                            result[i++] = oprStack.pop();
                        }
                        oprStack.push(s); // 将要入栈符号压入栈中
                    }
                }
            }
        }
        if (oprStack.stackLength() > 0) {
            while (!oprStack.stackEmpty()) {
                result[i++] = oprStack.pop();
            }
        }
        return cutArray(result, i);
    }

    /**
     * 比较两个运算符，如果两个符号中包含括号，则返回0，
     * 如果第一个符号优先级大于第二个符号，则返回一个大于0的数，
     * 如果第一个符号优先级小于第二个符号，则返回一个小于0的数，
     * 如果两个符号优先级相等，则返回0
     *
     * @param opr1 运算符1
     * @param opr2 运算符2
     * @return
     */
    public int compareOperator(String opr1, String opr2) {
        if (stringEmpty(opr1) || stringEmpty(opr2)) {
            return -127;
        }
        String[][] operators = {
                {"(", ")"}, {"+", "-"}, {"*", "/"}};
        int opr1Level = 0;
        int opr2Level = 0;
        for (int i = 0; i < operators.length; i++) {
            for (String s : operators[i]) {
                if (s.equals(opr1)) {
                    opr1Level = i;
                }
                if (s.equals(opr2)) {
                    opr2Level = i;
                }
            }
        }
        return (opr1Level == 0 || opr2Level == 0) ? 0 : (opr1Level - opr2Level);
    }

    /**
     * 判断字符串是否为空
     *
     * @param str
     * @return
     */
    public boolean stringEmpty(String str) {
        return str == null || "".equals(str);
    }

    /**
     * 将字符串表达式放到数组中
     *
     * @param infixExpr 中缀表达式字符串形式
     * @return 中缀表达式数组
     */
    public String[] string2ArrayExpr(String infixExpr) {
        String[] args = new String[1024];
        StringBuilder temp = new StringBuilder();
        int j = 0;
        // 把符号和数字放到数组中
        for (int i = 0; i < infixExpr.length(); i++) {
            // 判断是否是符号
            if (isOperator(infixExpr.charAt(i) + "")) {
                // 判断一下temp是否有值，如果有值，先将temp放入数组，再将符号放入数组
                if (temp.length() != 0) {
                    args[j++] = temp.toString();
                    temp.delete(0, temp.length());
                }
                args[j++] = infixExpr.charAt(i) + "";
            } else {
                // 如果是数字，将数字拼到temp后
                temp.append(infixExpr.charAt(i));
            }
        }
        if (temp.length() != 0) {
            args[j++] = temp.toString();
        }
        return cutArray(args, j);
    }

    /**
     * 中缀表达式输入
     *
     * @return
     */
    public String infixInput() {
        Scanner sc = new Scanner(System.in);
        String str = sc.next();
        return str;
    }

    /**
     * 后缀表达式输入
     *
     * @return
     */
    public String[] suffixInput() {
        String[] args = new String[1024];
        int i = 0;
        Scanner sc = new Scanner(System.in);
        String arg;
        while (!"=".equals(arg = sc.next())) {
            args[i++] = arg;
        }
        return cutArray(args, i);
    }

    /**
     * 截取指定长度的数组
     *
     * @param sourceArr    原数组
     * @param targetLength 要截取的数组的长度
     * @return
     */
    public String[] cutArray(String[] sourceArr, int targetLength) {
        String[] result = new String[targetLength];
        for (int i = 0; i < targetLength; i++) {
            result[i] = sourceArr[i];
        }
        return result;
    }
}
